/*! \file syscall.c 
 *	This files holds the implementations of all system calls.
 */

#include "kernel.h"




	case SYSCALL_VERSION:
{
	SYSCALL_ARGUMENTS.rax = KERNEL_VERSION;
	break;
}

	case SYSCALL_PRINTS:
{
	kprints((char*) (SYSCALL_ARGUMENTS.rdi));
	SYSCALL_ARGUMENTS.rax = ALL_OK;
	break;
}

	case SYSCALL_PRINTHEX:
{
	kprinthex(SYSCALL_ARGUMENTS.rdi);
	SYSCALL_ARGUMENTS.rax = ALL_OK;
	break;
}

	case SYSCALL_PRINTAT:
{
	int row = SYSCALL_ARGUMENTS.rdi;
	int col = SYSCALL_ARGUMENTS.rsi;
	char *str = (char*) (SYSCALL_ARGUMENTS.rdx);

	if (kprintat(row, col, str)) { // will mutate 
		SYSCALL_ARGUMENTS.rax = ALL_OK;
	} else {
		SYSCALL_ARGUMENTS.rax = ERROR;
	}

	break;
}

	case SYSCALL_DEBUGGER:
{
	/* Enable the bochs iodevice and force a return to the debugger. */
	outw(0x8a00, 0x8a00);
	outw(0x8a00, 0x8ae0);

	SYSCALL_ARGUMENTS.rax = ALL_OK;
	break;
}

/**
 *	The terminate system call terminates the currently running thread. The system call does 
 *	not take any parameters and will never return to the caller. It does thus not return any 
 *	values. Terminates the process when there are no threads left.
 */
	case SYSCALL_TERMINATE:
{
	/**
	 * TODO:
	 * 1. If killing the parent of some child processes, the parent_pid of the
	 * child processes should be set to the parent of the terminated process.
	 * 2. When killing a process the semaphore_table should be emptied of 
	 * semaphores belonging to that process.
	 */

	/* This will find the currently running thread, that we need to terminate.
	 * Also, the thread will be removed from the ready queue. */

	//	LOCK4WRITE(&CPU_private_table_lock);
	const int terminating_thread = CPU_private_table[get_processor_index()].thread_index;
	CPU_private_table[get_processor_index()].thread_index = -1;
	//	RELEASE_LOCK(&CPU_private_table_lock);

	MENSURE(terminating_thread != -1, "Idle thread should never be terminated",{break;});


	LOCK4WRITE(&thread_table_lock);
	int pid = thread_table[terminating_thread].data.owner; // The pid of the owner process of the thread.

	/* Terminate currently running thread */
	thread_table[terminating_thread].data.owner = -1; // The thread is now free to be allocated again.
	long start_stack = thread_table[terminating_thread].data.start_stack;
	RELEASE_LOCK(&thread_table_lock);

	LOCK4WRITE(&process_table_lock);
	/* Terminate the calling process if needed */
	process_table[pid].threads--; // One thread of the process is removed
	/* If the process has no more running threads, clean it up */
	int must_cleanup = 0;
	if (process_table[pid].threads == 0) {
		process_table[pid].parent = 0;
		must_cleanup = 1; // cleanup_process(pid);
	}
	RELEASE_LOCK(&process_table_lock);

	if (must_cleanup) {
		long semaphore_handle;
		LOCK4WRITE(&semaphore_table_lock);
		for (semaphore_handle = 0; semaphore_handle < MAX_NUMBER_OF_SEMAPHORES; semaphore_handle++) {
			if (semaphore_table[semaphore_handle].owner == pid) {
				semaphore_reset(semaphore_handle);
			}
		}
		RELEASE_LOCK(&semaphore_table_lock);
		cleanup_process(pid);
	} else if(start_stack != -1){
		kfree(start_stack-5);
	}

	/* Find thread to execute; invoke the scheduler.
	 */
	schedule = 1; // Force a re-schedule

	break;
}



/**
 *	The createprocess system call takes a parameter in the rdi register . This parameter is an 
 *	integer which corresponds to an executable program. The program will be be started as a 
 *	process by the system call. The system call returns in the rax register a value of ALL_OK 
 *	if a process was successfully created and run. A value of ERROR is returned if any error 
 *	occurred which prevented a new process to be created or run.
 */
	case SYSCALL_CREATEPROCESS:
{
	//	TRACE("Entering SYSCALL_CREATEPROCESS");

	const long rdi = SYSCALL_ARGUMENTS.rdi; // The index of the program we want to start
	int new_pid = 0;
	struct prepare_process_return_value newproc;

	/* Do this when condition in MENSURE is false */
#define RETURN_ERROR	{SYSCALL_ARGUMENTS.rax = ERROR; break;}	

	LOCK4READ(&process_table_lock);
	/* Find first available PID. This must be when a process has no running threads */
	for (new_pid = 0; new_pid < MAX_NUMBER_OF_PROCESSES; new_pid++) { // linear search
		if (process_table[new_pid].threads == 0)
			break;
	}
	RELEASE_LOCK(&process_table_lock);
	MENSURE(new_pid < MAX_NUMBER_OF_PROCESSES, "All PIDs occupied", RETURN_ERROR);

	/* Load program into memmory */
	newproc = prepare_process(executable_table[rdi].elf_image, new_pid, executable_table[rdi].memory_footprint_size);
	MENSURE(newproc.first_instruction_address != 0, "prepare_process failed", RETURN_ERROR);
	//	MENSURE(newproc.page_table_address != 0,		"prepare_process failed",  CREATEPROCESS_ERROR);	// XXX: When we implement VM

	/* Set the parent of the new process to be the calling process.
	 */
//	LOCK4READ(&CPU_private_table_lock);
	const int current_thread = CPU_private_table[get_processor_index()].thread_index;
//	RELEASE_LOCK(&CPU_private_table_lock);

	MENSURE(current_thread < MAX_NUMBER_OF_THREADS, "Too many threads", RETURN_ERROR);
	LOCK4WRITE(&process_table_lock);
	process_table[new_pid].parent = kgetpid(); // We are the parent to the new process
	process_table[new_pid].threads = 0; // kcreate_thread will increment the zero to one
	RELEASE_LOCK(&process_table_lock);

	SYSCALL_ARGUMENTS.rax = kcreate_thread(new_pid, newproc.first_instruction_address, -1);

	break;
}

	case SYSCALL_CREATETHREAD:
{
	const int rip = SYSCALL_ARGUMENTS.rdi;
	const int rsp = SYSCALL_ARGUMENTS.rsi;
	const int pid = kgetpid();

	SYSCALL_ARGUMENTS.rax = kcreate_thread(pid, rip, rsp);

	break;
}

	case SYSCALL_CREATESEMAPHORE:
{
	const int cnt = SYSCALL_ARGUMENTS.rdi;

	long semaphore_handle;
	// We start by finding a free slot in the semaphore_table
	LOCK4WRITE(&semaphore_table_lock);
	for (semaphore_handle = 0; semaphore_handle < sizeof (semaphore_table) / sizeof (semaphore_table[0]); semaphore_handle++) {
		if (semaphore_table[semaphore_handle].owner == -1)
			goto found;
	}
	RELEASE_LOCK(&semaphore_table_lock);
	SYSCALL_ARGUMENTS.rax = ERROR;
	break;

found:
	// The owner and the count of the new semaphore are set.
	semaphore_table[semaphore_handle].owner = kgetpid();
	semaphore_table[semaphore_handle].count = cnt;
	RELEASE_LOCK(&semaphore_table_lock);

	SYSCALL_ARGUMENTS.rax = semaphore_handle;

	break;
}

/**
 * The System call SYSCALL_SEMAPHOREDOWN decrements the semaphore if the
 * semaphore is greater than zero, else it blocks. The systemcall returns
 * an error if the parent process of the calling thread is not the owner
 * of the semaphore.
 */
	case SYSCALL_SEMAPHOREDOWN:
{
	//TRACE("SEMAPHOREDOWN is called");
	const long semaphore_handle = SYSCALL_ARGUMENTS.rdi;
	const int pid = kgetpid();
	LOCK4READ(&semaphore_table_lock);
	const int owner = semaphore_table[semaphore_handle].owner;
	const int count = semaphore_table[semaphore_handle].count;
	RELEASE_LOCK(&semaphore_table_lock);
	// Proceed if the parent process of the calling thread owns the semaphore.
	if (owner == pid) {
		// If the value of the semaphore is Zero the calling thread should be blocked.
		if (count == 0) {
			/* 
			 * If the  semaphore is Zero the calling thread should be blocked
			 * and saved onto some data structure that is checked if a semaphore
			 * released.
			 * and the scehduler should run.
			 */
			SYSCALL_ARGUMENTS.rax = semaphore_enqueue(get_current_thread(), semaphore_handle);
			schedule = 1;
		} else {
			// If the value of the semaphore is not Zero decrement the value of the semaphore.
			LOCK4WRITE(&semaphore_table_lock);
			semaphore_table[semaphore_handle].count--;
			RELEASE_LOCK(&semaphore_table_lock);
			SYSCALL_ARGUMENTS.rax = ALL_OK;
		}
	} else {
		SYSCALL_ARGUMENTS.rax = ERROR;
	}

	break;
}

/**
 * SYSCALL_SEMAPHOREUP increments the given semaphore if no blocked
 * threads are waiting for the semaphore.The systemcall first checks if the
 * parent process of the calling thread is the owner of the semaphore.
 */
	case SYSCALL_SEMAPHOREUP:
{
	//TRACE("SEMAPHOREUP is called");
	const long semaphore_handle = SYSCALL_ARGUMENTS.rdi;
	const int pid = kgetpid();
	LOCK4READ(&semaphore_table_lock);
	const int owner = semaphore_table[semaphore_handle].owner;
	const int count = semaphore_table[semaphore_handle].count;
	RELEASE_LOCK(&semaphore_table_lock);
	// Proceed if the parent process of the calling thread owns the semaphore.
	if (owner == pid) {
		// If the value of the semaphore is Zero the calling thread should be blocked.
		if (count == 0) {
			const int thread_index = semaphore_dequeue(semaphore_handle);
			if (thread_index != -1) { // The returned thread index is -1 if the queue is empty.
				LOCK4WRITE(&ready_queue_lock);
				LOCK4WRITE(&thread_table_lock);
				thread_queue_enqueue(&ready_queue, thread_index);
				RELEASE_LOCK(&thread_table_lock);
				RELEASE_LOCK(&ready_queue_lock);
			}
		} else {
			LOCK4WRITE(&semaphore_table_lock);
			semaphore_table[semaphore_handle].count++;
			RELEASE_LOCK(&semaphore_table_lock);
		}
		SYSCALL_ARGUMENTS.rax = ALL_OK;
	} else {
		SYSCALL_ARGUMENTS.rax = ERROR;
	}
	break;
}

/**
 * SYSCALL_CREATEMAILBOX creates a mailbox belonging to the calling process.
 */
	case SYSCALL_CREATEMAILBOX:
{
	void (*handler)(void) = (void*) SYSCALL_ARGUMENTS.rdi;
	const int port_id = SYSCALL_ARGUMENTS.rsi;
	SYSCALL_ARGUMENTS.rax = kcreate_mailbox(handler, port_id);
	break;
}

/**
 * SYSCALL_SENDMSG sends a message to the destination port. The system call
 * can only send a message if the process has a mailbox of its own, so a 
 * reply is possible.
 */
	case SYSCALL_SENDMSG:
{
	struct message* m = (struct message*)SYSCALL_ARGUMENTS.rdi;
	SYSCALL_ARGUMENTS.rax = ksend_msg(m);
	break;
}

/**
 * SYSCALL_RETRIEVEMSG is called from the thread created by the send_message
 * system call.
 */
	case SYSCALL_RETRIEVEMSG:
{
	struct message* m = (struct message*)SYSCALL_ARGUMENTS.rdi;
	const int port_id = SYSCALL_ARGUMENTS.rsi;
	SYSCALL_ARGUMENTS.rax = kretrieve_msg(m,port_id);
	break;
}

/**
 * SYSCALL_DELETEMAILBOX deletes the specified mailbox, if the calling process
 * owns the port/mailbox.
 */
	case SYSCALL_DELETEMAILBOX:
{
	const int port_id = SYSCALL_ARGUMENTS.rdi;
	SYSCALL_ARGUMENTS.rax = kdelete_mailbox(port_id);
	break;
}